package com.hxzy.common.aspect;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.hxzy.common.annon.Log;
import com.hxzy.common.security.AdminInfoUtil;
import com.hxzy.common.util.IpUtil;
import com.hxzy.common.util.ServletUtils;
import com.hxzy.system.entity.SysOperLog;
import com.hxzy.system.feign.SystemServiceFeignClient;
import com.hxzy.system.vo.AdminLoginVO;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Date;
import java.util.Map;

/**
 * 日志保存到数据库的  AOP  增强类(横切代码，通用代码)
 */
@Aspect
@Lazy
@Component
public class LogDataBaseAspect {


    /**
     * 远程调用systemservice服务
     */
    @Autowired
    private SystemServiceFeignClient systemServiceFeignClient;

    //1、定义  pointcut 切入,  哪些类中的哪些方法做增强
    @Pointcut(value = "@annotation(com.hxzy.common.annon.Log)")
    private void  pointCutExpression(){
    }


    /**
     * https://docs.spring.io/spring-framework/docs/5.2.22.RELEASE/spring-framework-reference/core.html#aop-ataspectj-advice-params-names
     * https://docs.spring.io/spring-framework/docs/5.2.22.RELEASE/spring-framework-reference/core.html#aop-advice-after-returning
     * 执行controller成功的时候
     * @param joinPoint
     * @param log
     * @param retVal  成功返回结果
     */
    @AfterReturning(pointcut = "pointCutExpression() && @annotation(log)",returning="retVal")
    public void logSuccessReturn(JoinPoint joinPoint, Log log, Object retVal){
        this.writeDB(joinPoint,log,null,retVal);
    }

    /**
     * https://docs.spring.io/spring-framework/docs/5.2.22.RELEASE/spring-framework-reference/core.html#aop-advice-after-throwing
     * 执行controller失败的时候
     * @param joinPoint
     * @param log
     */
    @AfterThrowing(pointcut = "pointCutExpression() && @annotation(log)",throwing="e")
    public void logError(JoinPoint joinPoint, Log  log, Exception e){
        this.writeDB(joinPoint,log,e,null);
    }


    /**
     * 写入参数到数据库
     * @param joinPoint
     * @param log
     * @param e
     * @param retVal
     */
    private void writeDB(JoinPoint joinPoint, Log  log, Exception e,Object retVal){
        SysOperLog sysLog=new SysOperLog();

        sysLog.setTitle(log.title());
        sysLog.setBusinessType(log.businessType().getCode());
        sysLog.setOperTime(new Date());

        //用户，shopid,商家名称  mybatis-plus 自动填充来实现


        // 请求方法 method (某个类的中某个方法)
//        String className=joinPoint.getTarget().getClass().getName();
//        String methodName=joinPoint.getSignature().getName();

        String methodName=joinPoint.getSignature().toString();
        String[] strArr = methodName.split(" ");
        sysLog.setMethod(  strArr[1]);

        //IP地址
        String ip= IpUtil.getIpAddr(ServletUtils.getRequest());
        sysLog.setOperIp(ip);

        //得到当前用户
        sysLog.setOperName(AdminInfoUtil.getRealName());
        sysLog.setDeptName(AdminInfoUtil.getDeptName());


        //请求参数 (最大字符串长度是2000)  有bug,  提交数据格式  application/json获取不到值
//        Map<String, String[]>  params= cn.hutool.extra.servlet.ServletUtil.getParams(ServletUtil.getRequest());
//        String jsonParam=JSONUtil.toJsonStr(params);
//        sysLog.setParams(StrUtil.maxLength(jsonParam,2000));

        //请求方式
        String requestMethod = ServletUtils.getRequest().getMethod();
        sysLog.setRequestMethod(requestMethod);

        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)
                || HttpMethod.DELETE.name().equals(requestMethod)|| HttpMethod.GET.name().equals(requestMethod)){
            String params = argsArrayToString(joinPoint.getArgs());
            sysLog.setOperParam(StrUtil.maxLength(params, 2000));
        }



        //没有异常
        if(e==null){
            sysLog.setStatus(0);
            //记录输出结果
            if(log.isSaveResponseData()){
                // 走fastjson框架
                String jsonResult= JSON.toJSONString(retVal, new LogSenstiveFastJsonFilter());
                sysLog.setJsonResult(jsonResult);
            }

        }else{
            sysLog.setStatus(1);
            //记录异常
            sysLog.setErrorMsg(e.getMessage());
        }

        this.systemServiceFeignClient.addLog(sysLog);

    }


    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0)  {
            for (Object o : paramsArray)
            {
                // 有些参数我们是不需要的
                if ( o!=null && !isFilterObject(o)) {
                    try
                    {
                        //要使用第三套的序列化框架 hutool内置json
                        String jsonObj = JSONUtil.toJsonStr(o);
                        params += jsonObj.toString() + " ";
                    }
                    catch (Exception e)
                    {
                    }
                }
            }
        }
        return params.trim();
    }


    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象，则返回true；否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o)
    {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            // 多文件上传   MultipartFile[]
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        }
        else if (Collection.class.isAssignableFrom(clazz))
        {
            Collection collection = (Collection) o;
            for (Object value : collection)
            {
                // 文件上传  List<MultipartFile>
                return value instanceof MultipartFile;
            }
        }
        else if (Map.class.isAssignableFrom(clazz))
        {
            Map map = (Map) o;
            for (Object value : map.entrySet())
            {
                Map.Entry entry = (Map.Entry) value;
                //Map值包含 MultipartFile
                return entry.getValue() instanceof MultipartFile;
            }
        }

        return o instanceof MultipartFile || o instanceof HttpServletRequest ||
                o instanceof HttpServletResponse || o instanceof BindingResult;
    }

}
